package com.jinke.api.modules.app.util;

import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.net.Inet4Address;
import java.net.UnknownHostException;
import java.util.Date;

/**
 * id生成器
 */
public class IdWorkerUtil {
    protected static final Log log = LogFactory.getLog(IdWorkerUtil.class);
    private long workerId;
    private long sequence = 0L;
    private long twepoch = 1451577600000L;
    private static long workerIdBits = 10L;
    public static long maxWorkerId = 0xFFFFFFFF ^ -1L << (int) workerIdBits;
    private long sequenceBits = 12L;
    private long workerIdShift = this.sequenceBits;
    private long timestampLeftShift = this.sequenceBits + workerIdBits;
    private long sequenceMask = 0xFFFFFFFF ^ -1L << (int) this.sequenceBits;
    private long lastTimestamp = -1L;

    private static IdWorkerUtil idWorker;

    static {
        idWorker = new IdWorkerUtil(getWorkId());
    }
    /**
     * 生成验证码
     *
     * @return
     */
    public static String getCode() {
        long id = idWorker.nextId();
        String idStr = Long.toString(id);
        return idStr.substring(idStr.length() - 6, idStr.length());
    }

    /**
     * 生成token
     *
     * @return
     */
    public static String getToken() {
        long id = idWorker.nextId();
        return Long.toString(id);
    }

    public IdWorkerUtil(long workerId) {
        if ((workerId > maxWorkerId) || (workerId < 0L)) {
            throw new IllegalArgumentException(
                    String.format("worker Id can't be greater than %d or less than 0",
                            new Object[]{Long.valueOf(maxWorkerId)}));
        }
        this.workerId = workerId;
        log.info(String.format(
                "worker starting. timestamp left shift %d, worker id bits %d, sequence bits %d, workerid %d",
                new Object[]{Long.valueOf(this.timestampLeftShift), Long.valueOf(workerIdBits),
                        Long.valueOf(this.sequenceBits), Long.valueOf(workerId)}));
    }

    public synchronized Long nextId() {
        long timestamp = timeGen();
        if (timestamp < this.lastTimestamp) {
            // 只打印warn信息
            log.warn(String.format("clock is moving backwards.  warnning requests until %d.",
                    new Object[]{Long.valueOf(this.lastTimestamp)}));
            this.lastTimestamp = timestamp;
        }
        if (this.lastTimestamp == timestamp) {
            this.sequence = (this.sequence + 1L & this.sequenceMask);
            if (this.sequence == 0L) {
                timestamp = tilNextMillis(this.lastTimestamp);
            }
        } else {
            this.sequence = 0L;
        }
        this.lastTimestamp = timestamp;
        return Long.valueOf(timestamp - this.twepoch << (int) this.timestampLeftShift
                | this.workerId << (int) this.workerIdShift | this.sequence);
    }

    private static Long getWorkId() {
        try {
            String hostAddress = Inet4Address.getLocalHost().getHostAddress();
            int[] ints = StringUtils.toCodePoints(hostAddress);
            int sums = 0;
            for (int b : ints) {
                sums += b;
            }
            return (long) (sums % 32);
        } catch (UnknownHostException e) {
            // 如果获取失败，则使用随机数备用
            return RandomUtils.nextLong(0, 31);
        }
    }


    protected long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    protected long timeGen() {
        return System.currentTimeMillis();
    }

    /**
     * id to 日期
     *
     * @param id
     * @return
     */
    public Date id2Time(long id) {
        long timestamp = id >> this.timestampLeftShift;
        return new Date(timestamp + this.twepoch);

    }

    /**
     * 生成Id 10位数字
     *
     * @return
     */
    public static String getId() {
        long id = idWorker.nextId();
        String idStr = Long.toString(id);
        return idStr.substring(idStr.length() - 10, idStr.length());
    }

    public static Long getFullId() {
        return idWorker.nextId();
    }

    /**
     * 过滤0打头
     * 生成Id 10位数字
     * @return
     */
    public static String getTrsCode() {
        String trsId = getId();
        if (!trsId.startsWith("0")) {
            return trsId;
        }

        try {
            //和时间有关  等会再生成
            Thread.sleep(50);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        return getTrsCode();
    }

    public static void main(String[] args) {
        for (int i = 0; i < 99; i++) {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(getTrsCode());
        }
    }
}
